<?php
/* easy rdf */

class Thing {
  var $_uri;
  var $_properties;
  var $_property_map;
  var $_prefix_map;

  function Thing($uri) {
    $this->_uri = $uri;
    $this->_properties = array();
    $this->_property_map = array();
    $this->_prefix_map = array();
  } 

  function get_uri() {
    return $this->_uri;
  }

  function get_property_details( $propertyUri ) {
    if ( array_key_exists( $propertyUri, $this->_property_map) ) {
      $propertyName = $this->_property_map[ $propertyUri ]['name'];
      if ( array_key_exists( 'functional', $this->_property_map[ $propertyUri ]) ) {
        $propertyFunctional = $this->_property_map[ $propertyUri ]['functional'];
      }
      else {
        $propertyFunctional = FALSE;
      }
    }
    else {
      $propertyName = NULL;
      $propertyFunctional = FALSE;
      if ( is_array(  $this->_prefix_map ) ) {
        foreach ( $this->_prefix_map as $uri=>$prefix) {
          if ( strpos( $propertyUri, $uri ) === 0) {
            $propertyName = $prefix . '_' . substr( $propertyUri, strlen($uri) );
            break;
          }
        }
      }
      $this->_property_map[ $propertyUri ] = array( 'name' => $propertyName, 'functional' => $propertyFunctional );
    }


    return array( $propertyName, $propertyFunctional );
  }

  function get_property_value( $object, $triples, &$things ) {
    if ( $object['type'] == 'literal' ) {
      $propertyValue = $object['val'];
    }
    else {
      $obj_val = $object['type'] == 'uri' ? $object['uri'] : $object['bnode_id'];

      if ( array_key_exists( $obj_val , $things ) ) {
        $propertyValue = $things[ $obj_val ];
      }
      else {
        $propertyValue = new Thing( $obj_val );   
        $propertyValue->set_property_mapping( $this->_property_map );
        $things[ $obj_val ] = $propertyValue;
        $propertyValue->read_from_triple_array( $triples, $things );
      }
    }

    return $propertyValue;
  }
  
  function read_from_triple_array( $triples, &$things = array() ) {
    if ( ! is_array( $triples ) ) return;
    if ( ! array_key_exists( $this->_uri , $things ) ) {
      $things[ $this->_uri ] = $this;
    }
    for($i=0,$i_max=count($triples);$i<$i_max;$i++) {
      $subject = $triples[$i]['s']['type']=='uri' ? $triples[$i]['s']['uri']  : $triples[$i]['s']['bnode_id'] ;
      if ( $subject == $this->_uri ) {
        $propertyUri = $triples[$i]['p'];
  
        list ($propertyName, $propertyFunctional ) = $this->get_property_details( $propertyUri );
  
        if ( $triples[$i]['o']['type'] == 'literal' ) {
          $this->_property_map[ $propertyUri ]['object'] = FALSE;
        }
        else {
          $this->_property_map[ $propertyUri ]['object'] = TRUE;
        }
        $propertyValue = $this->get_property_value( $triples[$i]['o'], $triples, $things );
  
        if ( $propertyFunctional ) {
          $this->_properties[ $propertyUri ] = $propertyValue;
          $this->$propertyName = $propertyValue;
        }
        else {
          if ( ! array_key_exists( $propertyUri, $this->_properties) ) {
            $this->_properties[ $propertyUri ] = array();
            if ( $propertyName ) {
              $this->$propertyName = array();
            }
          }
  
          array_push($this->_properties[ $propertyUri ], $propertyValue);
          if ( $propertyName ) {
            array_push($this->$propertyName, $propertyValue);
          }
        }
  
      }
    }
  }

  function set_property_mapping( $property_map ) {
    if ( is_array( $property_map ) ) {
      $this->_property_map = $property_map;
      foreach ($this->_property_map as $predicate=>$info) {
        if ( array_key_exists('name', $info) && $info['name'] != NULL ) {
          if (array_key_exists('functional', $info) && $info['functional'] == true ) {
            $this-> $info['name'] = NULL;
          }
          else {
            $this-> $info['name'] = array();
          }
        }
      }
    }
  }

  function set_prefix_mapping( $prefix_map ) {
    $this->_prefix_map = $prefix_map;
  }
  
  function to_triple_array() {
    return array_values( $this->to_triple_index() );
  }

  function to_triple_index() {
    $triples = array();
    if ( substr($this->get_uri(),  0, 2) != '_:' ) {
      $subject = array('type'=>'uri', 'uri'=>$this->get_uri());
      $key_with_subject = '<' . $this->get_uri() . '> ';
    }
    else {
      $subject = array('type'=>'bnode', 'bnode_id'=>$this->get_uri());
      $key_with_subject = '_:' . $this->get_uri() . ' ';
    }


    foreach ($this->_property_map as $predicate=>$info) {
      if ( array_key_exists('name', $info) && $info['name'] != NULL ) {
        if ( NULL != $this->$info['name'] ) {
          if ( array_key_exists('functional', $info) && true == $info['functional'] ) {
            $values = array( $this->$info['name'] ); 
          }
          else {
            $values = $this->$info['name']; 
          }
        }
        else {
          $values = array();
        }
      }
      else {
        if ( array_key_exists($predicate, $this->_properties) ) {
          $values = $this->_properties[$predicate];
        }
        else {
          $values = array();
        }
      }

      foreach ($values as $value) {
        if  ( is_a( $value, 'Thing')  ) {
          if ( substr($value->get_uri(),  0, 2) != '_:' ) {
            $object = array('type'=>'uri', 'uri'=>$value->get_uri());
            $key = $key_with_subject . '<' . $predicate . '> <' . $value->get_uri();
          }
          else {
            $object = array('type'=>'bnode', 'bnode_id'=>$value->get_uri());
            $key = $key_with_subject . '<' . $predicate . '> _:' . $value->get_uri();
          }

          $triples = array_merge( $triples, $value->to_triple_index() );
        }
        else {
          $object = array('type'=>'literal', 'val'=>$value);
          $key = $key_with_subject . '<' . $predicate . '> "' . $value . '"';
        }

        if  ( ! array_key_exists( $key, $triples ) ) {

          $triples[$key] = array (
                          's' => $subject,
                          'p' => $predicate,
                          'o' => $object,
                      );
          }
      }
    }      

    return $triples;
  }

  function to_rdfxml() {

    $ns_prefixes = array('http://www.w3.org/1999/02/22-rdf-syntax-ns#'=>'rdf');

    if ( is_array( $this->_prefix_map ) ) {
      foreach ($this->_prefix_map as $ns=>$prefix) {
        $ns_prefixes[ $ns ] = $prefix;  
      }
    }

    $predicate_qnames = array();    
    $added_namespace_prefix_count = 0;

    $triples = $this->to_triple_array();

    foreach ($triples as $triple) {
      if ( ! array_key_exists( $triple['p'], $predicate_qnames) ) {

        if(preg_match("/(.*[\/|#|\:])([^\/|#|\:]+)/", $triple['p'], $matches)){
          $ns=$matches[1];
          $local_name=$matches[2];
          if ( ! array_key_exists( $ns, $ns_prefixes) ) {
            $ns_prefixes[$ns] = 'x' . $added_namespace_prefix_count;
            $added_namespace_prefix_count++;
          }
          $predicate_qnames[ $triple['p'] ] = $ns_prefixes[$ns] . ':' . $local_name;
        }
      }
    }

    $rdfxml = "<rdf:RDF\n";
    foreach ($ns_prefixes as $ns=>$prefix) {
      $rdfxml .= '  xmlns:' . $prefix . '="' . $ns . '"' . "\n";
    }
    $rdfxml .= ">\n";

    $subjectIndex = array();
    for($i=0,$i_max=count($triples);$i<$i_max;$i++) {
			$s_val=($triples[$i]['s']['type']=="uri")? $triples[$i]['s']["uri"] : $triples[$i]['s']["bnode_id"];
      if ( ! array_key_exists( $s_val, $subjectIndex ) ) {
        $subjectIndex[$s_val] = array();
      }
      
      array_push( $subjectIndex[$s_val] , $triples[$i] );
    }

    foreach ( $subjectIndex as $subject=>$triples) {
      $rdfxml .= '  <rdf:Description';
      if ( substr($subject,  0, 2) != '_:' ) {
        $rdfxml .= ' rdf:about="' . $subject . '">';
      }
      else {
        $rdfxml .= ' rdf:nodeID="' . substr( $subject, 2) . '">';
      }
      $rdfxml .= "\n";
  
      foreach ($triples as $triple) {
        $rdfxml .= '<' . $predicate_qnames[ $triple['p'] ];
  
        if ( $triple['o']['type'] == 'uri' ) {
          $rdfxml .= ' rdf:resource="' . htmlspecialchars($triple['o']['uri']) . '" />' . "\n";
        }
        elseif ( $triple['o']['type'] == 'literal' ) {
          if ( isset( $triple['o']['lang'] ) && $triple['o']['lang'] != '' ) {
            $rdfxml .= ' xml:lang="' . htmlspecialchars($triple['o']['lang']) . '"';
          }
    
          $rdfxml .= '>';
          $rdfxml .= htmlspecialchars($triple['o']['val'], ENT_NOQUOTES, 'UTF-8' );
          $rdfxml .= '</' . $predicate_qnames[ $triple['p'] ] .  ">\n";
        }
        elseif ( $triple['o']['type'] == 'bnode' ) {
          $rdfxml .= ' rdf:nodeID="' . substr( $triple['o']['bnode_id'], 2) . '" />' . "\n";
        }
  
      }
      $rdfxml .= '  </rdf:Description>';
    }

    $rdfxml .= '</rdf:RDF>';
    return $rdfxml;
  }


  function to_turtle() {

    $ns_prefixes = array('http://www.w3.org/1999/02/22-rdf-syntax-ns#'=>'rdf');

    foreach ($this->_prefix_map as $ns=>$prefix) {
      $ns_prefixes[ $ns ] = $prefix;  
    }

    $predicate_qnames = array();    
    $added_namespace_prefix_count = 0;

    $triples = $this->to_triple_array();

    foreach ($triples as $triple) {
      if ( ! array_key_exists( $triple['p'], $predicate_qnames) ) {

        if(preg_match("/(.*[\/|#|\:])([^\/|#|\:]+)/", $triple['p'], $matches)){
          $ns=$matches[1];
          $local_name=$matches[2];
          if ( ! array_key_exists( $ns, $ns_prefixes) ) {
            $ns_prefixes[$ns] = 'x' . $added_namespace_prefix_count;
            $added_namespace_prefix_count++;
          }
          $predicate_qnames[ $triple['p'] ] = $ns_prefixes[$ns] . ':' . $local_name;
        }
      }
    }

    $turtle = '';
    foreach ($ns_prefixes as $ns=>$prefix) {
      $turtle .= '@prefix ' . $prefix . ': <' . $ns . "> .\n";
    }

    $subjectIndex = array();
    for($i=0,$i_max=count($triples);$i<$i_max;$i++) {
			$s_val=($triples[$i]['s']['type']=="uri")? $triples[$i]['s']["uri"] : $triples[$i]['s']["bnode_id"];
      if ( ! array_key_exists( $s_val, $subjectIndex ) ) {
        $subjectIndex[$s_val] = array();
      }
      
      array_push( $subjectIndex[$s_val] , $triples[$i] );
    }

    foreach ( $subjectIndex as $subject=>$triples) {
      $turtle .= "\n";
      if ( substr($subject,  0, 2) != '_:' ) {
        $turtle .= '<' . $subject . ">";
      }
      else {
        $turtle .= "_:" . substr( $subject, 2);
      }
      $turtle .= "\n";
  
      foreach ($triples as $triple) {
        $turtle .= '  ' . $predicate_qnames[ $triple['p'] ] . ' ';
  
        if ( $triple['o']['type'] == 'uri' ) {
          $turtle .= '<' .$triple['o']['uri'] . '>';
        }
        elseif ( $triple['o']['type'] == 'literal' ) {
          $turtle .= '"' . addcslashes($triple['o']['val'], "\t\n\r\"\\") . '"';
          if ( isset( $triple['o']['lang'] ) && $triple['o']['lang'] != '' ) {
            $turtle .= '@' . $triple['o']['lang'];
          }
        }
        elseif ( $triple['o']['type'] == 'bnode' ) {
          $turtle .= ' _:' . substr( $triple['o']['bnode_id'], 2);
        }
  
          $turtle .= " .\n";
      }
    }

    return $turtle;
  }

  function make_thing($uri) {
    $thing = new Thing($uri);
    $thing->set_property_mapping( $this->_property_map );
    $thing->set_prefix_mapping( $this->_prefix_map );
    return $thing;
  }
}


?>